﻿#include "GLProgram.h"
#include <memory>

GLProgram* GLProgram::create(const char* vertShaderSrc, const char* fragShaderSrc)
{
    GLProgram* program = new GLProgram();

    if (program && program->init(vertShaderSrc, fragShaderSrc))
    {
        return program;
    }

    if (program)
    {
        delete program;
    }

    return nullptr;
}

GLint GLProgram::getAttribLocation(const char* name)
{
    return glGetAttribLocation(_program, name);
}

GLint GLProgram::getUniformLocation(const char* name)
{
    return glGetUniformLocation(_program, name);
}

void GLProgram::use()
{
    glUseProgram(_program);
}

GLProgram::GLProgram()
    :_program(0)
    ,_vertexShader(0)
    ,_fragmentShader(0)
{
}

GLProgram::~GLProgram()
{
    clearShader();

    if (glIsProgram(_program))
    {
        glDeleteProgram(_program);
        _program = 0;
    }
}

bool GLProgram::init(const char* vertShaderSrc, const char* fragShaderSrc)
{
    // 着色器对象句柄
    _program = glCreateProgram();

    if (vertShaderSrc)
    {
        // 编译顶点着色器取得编译后的句柄
        _vertexShader = loadShader(GL_VERTEX_SHADER, vertShaderSrc);
    }

    if (fragShaderSrc)
    {
        // 编译片段着色器取得编译后的句柄
        _fragmentShader = loadShader(GL_FRAGMENT_SHADER, fragShaderSrc);
    }

    if (glIsShader(_vertexShader))
    {
        glAttachShader(_program, _vertexShader);
    }
    else 
    {
        return false;
    }

    if (glIsShader(_fragmentShader))
    {
        glAttachShader(_program, _fragmentShader);
    }
    else
    {
        return false;
    }

    // 连接着色器对象
    glLinkProgram(_program);

    // 检查连接是否成功
    GLint linked;
    glGetProgramiv(_program, GL_LINK_STATUS, &linked);

    if (!linked)
    {
        GLint infoLen = 0;

        glGetProgramiv(_program, GL_INFO_LOG_LENGTH, &infoLen);

        if (infoLen > 1)
        {
            char* infoLog = (char*)malloc(sizeof(char) * infoLen);

            glGetProgramInfoLog(_program, infoLen, NULL, infoLog);
            printf("着色程序编译错误：%s\n", infoLog);

            free(infoLog);
        }

        clearShader();
        glDeleteProgram(_program);
        _program = 0;

        return false;
    }

    return true;
}

// 删除着色器
void GLProgram::clearShader()
{
    if (glIsShader(_vertexShader))
    {
        // 标记着色器为删除，当着色器不再链接任何对象时，它的内存才被释放
        glDeleteShader(_vertexShader);
        _vertexShader = 0;
    }

    if (glIsShader(_fragmentShader))
    {
        // 标记着色器为删除，当着色器不再链接任何对象时，它的内存才被释放
        glDeleteShader(_fragmentShader);
        _fragmentShader = 0;
    }
}

GLuint GLProgram::loadShader(GLenum type, const char* shaderSrc)
{
    GLuint shader;  // 指向着色器对象的句柄
    GLint complied; // 检查编译的查询结果

    // 创建着色器
    shader = glCreateShader(type);

    if (shader == 0) {
        return 0;
    }

    // 指定着色器源代码
    glShaderSource(shader, 1, &shaderSrc, 0);

    // 编译着色器
    glCompileShader(shader);

    // 检查编译是否成功
    glGetShaderiv(shader, GL_COMPILE_STATUS, &complied);

    if (!complied) {
        GLint infoLen = 0;

        // 获取日志长度
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);

        if (infoLen > 1) {
            // 日志缓冲区指针
            char* infoLog = (char*)malloc(sizeof(char) * infoLen);

            // 获取日志
            glGetShaderInfoLog(shader, infoLen, NULL, infoLog);
            printf("着色程序编译错误：%s\n", infoLog);

            // 释放内存
            free(infoLog);
        }

        // 删除着色对象
        glDeleteShader(shader);

        return 0;
    }

    return shader;
}

void GLProgram::printVertexAttribs()
{
    // 查询出着色器中变量的数量
    GLint linked;
    glGetProgramiv(_program, GL_ACTIVE_ATTRIBUTES, &linked);

    GLint   length;
    GLint   bufSize;
    GLenum  type;
    char    name[16];

    for (GLint i = 0; i < linked; i++)
    {
        // 查询着色器变量名称
        glGetActiveAttrib(_program, i, 16, &length, &bufSize, &type, name);

        // 获取着色器变量索引
        GLint location = glGetAttribLocation(_program, name);

        printf("变量名称:%s  索引:%d\n", name, location);
    }
}
